package com.project.easy.common.log.core.processor;


import com.project.easy.common.log.core.PersistentProcessor;
import com.project.easy.common.log.core.dto.FactoryWriteLog;
import com.project.easy.common.log.core.dto.WriteLogDto;
import com.project.easy.common.log.core.service.ILogQueryExtend;
import com.project.easy.common.sequencer.SnowflakeIdWorker;
import  com.project.easy.common.util.collection.CollectionUtil;
import  com.project.easy.common.util.constants.CommonConstant;
import  com.project.easy.common.util.evn.YqkjZysoftEnvironment;
import  com.project.easy.common.util.exception.BaseException;
import  com.project.easy.common.util.exception.print.PrintException;
import  com.project.easy.common.util.exception.sys.SysUnknowException;
import  com.project.easy.common.util.json.JacksonUtil;
import  com.project.easy.common.util.string.StringUtil;
import com.project.easy.common.log.annotation.Logger;
import com.project.easy.common.log.annotation.Logging;

import com.project.easy.common.log.util.ExpressionTool;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.EvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @ClassName LogAspect
 * @Description
 * @Author GreatorMan.cool
 * @Date 2021/3/25 16:43
 * @Version 1.0
 **/
@Aspect
@Component
public class LogProcessor implements Ordered {

    private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(LogProcessor.class);

    @Autowired(required = false)
    private ILogQueryExtend logQueryExtend;

    private  static  final String SUCCESS = "200";
    /**
     * 正常日志
     * @return  执行返回参数
     * @param jp 切面
     * @throws Throwable 异常
     */
    @Around("@annotation(com.project.easy.common.log.annotation.Logging)")
    public Object doLogging(ProceedingJoinPoint jp) throws Throwable {
        Logger logger = AnnotationUtils.findAnnotation(jp.getTarget().getClass(), Logger.class);
        Method method = ((MethodSignature) jp.getSignature()).getMethod();
        String traceId = SnowflakeIdWorker.nextIdStr();
        Thread.currentThread().setName(traceId);
        if (Objects.isNull(logger)) {
             logger = AnnotationUtils.findAnnotation(method, Logger.class);
        }
        Logging logging = AnnotationUtils.findAnnotation(method, Logging.class);
        StopWatch stopWatch = new StopWatch(method.getName());
        stopWatch.start();
        Object[] args = jp.getArgs();
        if (LOG.isInfoEnabled()) {
            LOG.info("调用开始:{}", method.getName());
            LOG.info("入参:{}", CollectionUtil.isNull(args) ?  "无参" : JacksonUtil.toJsonString(args));
        }
        String errrorCode = SUCCESS;
        String errorMsg = "";
        Object object = null;
        try {
            object = jp.proceed();
            return  object;
        } catch (Throwable e) {
            if (e instanceof BaseException) {
                BaseException baseException = (BaseException) e;
                errrorCode = baseException.getSubCode();
                errorMsg = baseException.getSubMsg();
                throw e;
            }
            PrintException.print(e);
            throw new SysUnknowException(e.getMessage());
        } finally {
            stopWatch.stop();
            Boolean hasDb = logger.hasDb() && logging.hasDb();
            if (LOG.isInfoEnabled()) {
                LOG.info("调用结束{},耗时:{}, 输出:{},是否入库:{}", method.getName(), stopWatch.getTotalTimeMillis(),
                        Objects.isNull(object) ? "" : JacksonUtil.toJsonString(object), hasDb);
            }
            if (hasDb) {
                WriteLogDto instance = FactoryWriteLog.instance();
                instance.setSys(YqkjZysoftEnvironment.getStr(CommonConstant.YQKJ_SYS_CODE));
                instance.setModel(logger.model());
                instance.setTakeTime(stopWatch.getTotalTimeMillis());
                instance.setLogStatus(errrorCode);
//                String input = logging.input();
                instance.setContent(errorMsg);
                instance.setInput(JacksonUtil.toJsonString(args));
                instance.setTrackId(traceId);
                instance.setModelInfo(String.format("%s->%s", logger.model(), logging.value()));
                if (!Objects.isNull(logQueryExtend)) {
                    logQueryExtend.query(instance);
                }
                /*EvaluationContext context = ExpressionTool.getEvaluationContext(((MethodSignature) jp.getSignature()).getParameterNames(),
                        jp.getArgs(), object, instance.getUserInfo(), request);
                input = queryInputValue(input, context, request);*/
//                instance.setInput(input);
                String output = logging.output();
           /*     if (StringUtil.isNotBlank(output)) {
                    output =  ExpressionTool.getSpelValue(context, output);
                } else {
                    output = Objects.isNull(object) ? "" : object.toString();
                }*/
                instance.setContent(StringUtil.isBlank(errorMsg) ? output : errorMsg);
                String hasResult = logging.hasResult();
                PersistentProcessor.write(instance);
            }
        }
    }
    /**
     *
     * @param input 入参
     * @param context 上下文
     * @param request 请求参数
     * @return 返回值
     * @throws IOException Io 异常
     */
    private String queryInputValue(String input, EvaluationContext context, HttpServletRequest request) throws IOException {
        input =  ExpressionTool.getSpelValue(context, input);
        return input;
    }

    /**
     * Get the order value of this object.
     * <p>Higher values are interpreted as lower priority. As a consequence,
     * the object with the lowest value has the highest priority (somewhat
     * analogous to Servlet {@code load-on-startup} values).
     * <p>Same order values will result in arbitrary sort positions for the
     * affected objects.
     *
     * @return the order value
     * @see #HIGHEST_PRECEDENCE
     * @see #LOWEST_PRECEDENCE
     */
    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE + 1;
    }
}
